﻿using System;
using Wizard.Compiler.Parsers;
using Wizard.Runtime;
using Wizard.Compiler.Tokenizers;

namespace Wizard.Functions
{
    public abstract class NumericalOperator<K> : TwoParams<K, K, K>
    {
        private readonly RecalcDelegate mRecalcDelegate;

        public NumericalOperator(TokenType op, IHasValue<K> param1, IHasValue<K> param2)
            : base("+", FunctionType.@operator, param1, param2)
        {
            // this is compile time
            switch (op)
            {
                case TokenType.operator_plus:
                    mRecalcDelegate = Add;
                    break;
                case TokenType.operator_minus:
                    mRecalcDelegate = Sub;
                    break;
                case TokenType.operator_mul:
                    mRecalcDelegate = Mul;
                    break;
                case TokenType.operator_div:
                    mRecalcDelegate = Div;
                    break;
                default:
                    throw new TokenizerException("Invalid operator");
            }
        }

        protected override K RecalcValue(IRunContext context)
        {
            return mRecalcDelegate(context, mParam1.Value(context), mParam2.Value(context));
        }

        protected abstract K Add(IRunContext context, K x, K y);
        protected abstract K Sub(IRunContext context, K x, K y);
        protected abstract K Mul(IRunContext context, K x, K y);
        protected abstract K Div(IRunContext context, K x, K y);

        #region Nested type: RecalcDelegate

        private delegate K RecalcDelegate(IRunContext context, K a, K b);

        #endregion
    }

    public class Int32Operator : NumericalOperator<Int32>
    {
        public Int32Operator(TokenType op, IHasValue<Int32> param1, IHasValue<Int32> param2)
            : base(op, param1, param2)
        {
        }

        protected override Int32 Add(IRunContext context, Int32 x, Int32 y)
        {
            return x + y;
        }

        protected override Int32 Sub(IRunContext context, Int32 x, Int32 y)
        {
            return x - y;
        }

        protected override Int32 Mul(IRunContext context, Int32 x, Int32 y)
        {
            return x * y;
        }

        protected override Int32 Div(IRunContext context, Int32 x, Int32 y)
        {
            return x / y;
        }
    }

    public class Int64Operator : NumericalOperator<Int64>
    {
        public Int64Operator(TokenType op, IHasValue<Int64> param1, IHasValue<Int64> param2)
            : base(op, param1, param2)
        {
        }

        protected override Int64 Add(IRunContext context, Int64 x, Int64 y)
        {
            return x + y;
        }

        protected override Int64 Sub(IRunContext context, Int64 x, Int64 y)
        {
            return x - y;
        }

        protected override Int64 Mul(IRunContext context, Int64 x, Int64 y)
        {
            return x * y;
        }

        protected override Int64 Div(IRunContext context, Int64 x, Int64 y)
        {
            return x / y;
        }
    }

    public class DoubleOperator : NumericalOperator<double>
    {
        public DoubleOperator(TokenType op, IHasValue<double> param1, IHasValue<double> param2)
            : base(op, param1, param2)
        {
        }

        protected override double Add(IRunContext context, double x, double y)
        {
            return x + y;
        }

        protected override double Sub(IRunContext context, double x, double y)
        {
            return x - y;
        }

        protected override double Mul(IRunContext context, double x, double y)
        {
            return x * y;
        }

        protected override double Div(IRunContext context, double x, double y)
        {
            return x / y;
        }
    }

    public class DecimalOperator : NumericalOperator<Decimal>
    {
        public DecimalOperator(TokenType op, IHasValue<Decimal> param1, IHasValue<Decimal> param2)
            : base(op, param1, param2)
        {
        }

        protected override Decimal Add(IRunContext context, Decimal x, Decimal y)
        {
            return x + y;
        }

        protected override Decimal Sub(IRunContext context, Decimal x, Decimal y)
        {
            return x - y;
        }

        protected override Decimal Mul(IRunContext context, Decimal x, Decimal y)
        {
            return x * y;
        }

        protected override Decimal Div(IRunContext context, Decimal x, Decimal y)
        {
            return x / y;
        }
    }
}